<template>
  <view
    class="v-textarea-container"
    :style="containerStyle"
  >
    <textarea
      v-model="inputValue"
      class="v-textarea"
      :style="[{ textAlign: align }, customStyle]"
      :type="type"
      :password="password"
      :placeholder="placeholder"
      :placeholder-style="placeholderStyleString"
      :placeholder-class="placeholderClass"
      :disabled="disabled"
      :focus="focus"
      :cursor-spacing="cursorSpacing"
      :maxlength="length"
      @input="inputHandle"
      @focus="focusHandle"
      @blur="blurHandle"
      @confirm="(event: Event) => $emit('confirm', event)"
    />
    <!-- #ifndef MP-ALIPAY -->
    <view
      v-if="maxlength"
      class="counter"
    >
      {{ `${inputValue?.length ?? 0}/${maxlength}` }}
    </view>
    <!-- #endif -->
  </view>
</template>

<script setup lang="ts">
import type { CSSProperties } from 'vue';

/** https://uniapp.dcloud.net.cn/component/input.html#input */
const props = withDefaults(
  defineProps<{
    modelValue: string;
    replace?: RegExp;
    customStyle?: CSSProperties;
    containerStyle?: CSSProperties;
    type?: string;
    password?: boolean;
    placeholder?: string;
    placeholderStyle?: CSSProperties;
    placeholderClass?: string;
    disabled?: boolean;
    maxlength?: number | string;
    focus?: boolean;
    confirmType?: string;
    /** 获得焦点上划高度，单位px */
    cursorSpacing?: number;
    align?: 'left' | 'center' | 'right';
  }>(),
  {
    modelValue: '',
    replace: undefined,
    customStyle: () => ({}),
    containerStyle: () => ({}),
    type: undefined,
    password: false,
    placeholder: '',
    placeholderStyle: undefined,
    placeholderClass: 'placeholder',
    disabled: false,
    maxlength: undefined,
    focus: false,
    confirmType: undefined,
    cursorSpacing: 12,
    align: 'left'
  }
);

const emits = defineEmits<{
  'update:model-value': [val: string];
  input: [val: string];
  focus: [];
  blur: [];
  confirm: [val: Event];
}>();

const inputValue = ref('');

const modelValue = toRef(props, 'modelValue');
const replace = toRef(props, 'replace');
const maxlength = toRef(props, 'maxlength');

watch(
  modelValue,
  (val) => {
    if (val !== inputValue.value) {
      inputValue.value = val;
    }
  },
  { immediate: true }
);

const length = computed(() => {
  switch (typeof maxlength.value) {
    case 'string': {
      const length = parseInt(maxlength.value);
      return isNaN(length) ? undefined : length;
    }
    case 'number':
      return maxlength.value;
    default:
      return -1;
  }
});
const validate = inject('v-form-item-validate', () => undefined) as (
  changeFirst?: boolean
) => void;
const setFocus = inject('v-form-item-set-focus', () => undefined) as (
  focus: boolean
) => void;
const inputHandle = async (event: Event) => {
  const value = (event as InputEvent).detail.value;
  inputValue.value = value;
  const formated = replace.value ? value.replace(replace.value, '') : value;
  await new Promise((resolve) => nextTick(() => resolve(true)));
  inputValue.value = formated;
  emits('update:model-value', formated);
  await new Promise((resolve) => nextTick(() => resolve(true)));
  emits('input', formated);
  nextTick(() => {
    validate(false);
  });
};
const focusHandle = () => {
  setFocus(true);
  emits('focus');
};
const blurHandle = () => {
  setFocus(false);
  validate();
  emits('blur');
};

const customStyle = toRef(props, 'customStyle');
const placeholderStyle = toRef(props, 'placeholderStyle');
const placeholderStyleString = computed(() => {
  if (!placeholderStyle.value) {
    const list: string[] = [];
    for (const key in customStyle.value) {
      if (!key.includes('color')) continue;
      if (!key.includes('padding')) continue;
      const keyName = key.replace(
        /[A-Z]/g,
        (match) => `-${match.toLowerCase()}`
      );
      list.push(`${keyName}: ${customStyle.value[key as keyof CSSProperties]}`);
    }
    if (!list.some((item) => item.startsWith('color'))) {
      list.push('color: rgb(201, 205, 212)');
    }
    return list.join(';');
  } else {
    const list = Object.entries(placeholderStyle.value).map(([key, value]) => {
      const keyName = key.replace(
        /[A-Z]/g,
        (match) => `-${match.toLowerCase()}`
      );
      return `${keyName}: ${value}`;
    });
    if (!list.some((item) => item.startsWith('color'))) {
      list.push('color: rgb(201, 205, 212)');
    }
    return list.join(';');
  }
});
</script>

<style lang="scss" scoped>
.v-textarea-container {
  position: relative;
  line-height: 1.3rem;
  box-sizing: border-box;
  border: 1px solid #cccccc;
  border-radius: 16rpx;
  padding: 24rpx;
  .v-textarea {
    padding: 0;
    background: transparent;
    font-size: inherit;
    padding-bottom: 1.5em;
  }
  .counter {
    position: absolute;
    right: 0;
    bottom: -4rpx;
    color: #999999;
    font-size: 20rpx;
    line-height: 26rpx;
  }
}
</style>
